home *** CD-ROM | disk | FTP | other *** search
- #include "cache.h"
- #include <alloc.h>
- #include <mem.h>
-
- enum buffer_status {EMPTY, CLEAN, DIRTY};
-
- static unsigned access(struct cache *q, int write, struct cache_block *b)
- {
- unsigned e = (*q->drive_access)(write, b->drive, b->sector, b->data);
- if (e!=0)
- {
- q->error_sector = b->sector;
- q->error_drive = b->drive;
- }
- return e;
- }
-
- unsigned cache_access(struct cache *q, int write, unsigned drive,
- unsigned sector, void *buffer)
- {
- struct
- {
- struct cache_block *previous;
- struct cache_block *current;
- } b, empty, clean, dirty;
- /* find the matching block, and also the first empty, clean and dirty */
- /* blocks, all with a single pass through the list */
- b.previous = empty.current = clean.current = dirty.current = NULL;
- b.current = q->first;
- do
- {
- if (b.current->status == EMPTY)
- {
- if (empty.current == NULL) empty = b;
- }
- else if (b.current->sector == sector && b.current->drive == drive)
- break;
- else
- {
- if (b.current->status == CLEAN)
- {
- if (clean.current == NULL) clean = b;
- }
- else if (dirty.current == NULL) dirty = b;
- }
- b.previous = b.current;
- b.current = b.current->next;
- } while (b.current != NULL);
- /* if there is no matching block, assign one according to the rules */
- if (b.current == NULL)
- {
- if (empty.current != NULL) b = empty;
- else if (clean.current != NULL) b = clean;
- else
- {
- unsigned e;
- b = dirty;
- e = access(q, 1, b.current);
- if (e) return e;
- }
- b.current->status = EMPTY;
- b.current->sector = sector;
- b.current->drive = drive;
- }
- if (write)
- {
- memcpy(b.current->data, buffer, q->sector_size);
- b.current->status = DIRTY;
- }
- else
- {
- if (b.current->status == EMPTY)
- {
- unsigned e = access(q, 0, b.current);
- if (e) return e;
- b.current->status = CLEAN;
- }
- memcpy(buffer, b.current->data, q->sector_size);
- }
- /* put block at the end of the line */
- if (b.current != q->last)
- {
- if (b.previous == NULL) q->first = b.current->next;
- else b.previous->next = b.current->next;
- q->last->next = b.current;
- b.current->next = NULL;
- q->last = b.current;
- }
- return 0;
- }
-
- unsigned cache_flush_and_or_clear(struct cache *q, int drive, int options)
- {
- struct cache_block *b;
- for (b=q->first; b!=NULL; b=b->next)
- {
- if (drive<0 || b->drive==drive)
- {
- if (options&CACHE_FLUSH && b->status==DIRTY)
- {
- unsigned e = access(q, 1, b);
- if (e) return e;
- b->status = CLEAN;
- }
- if (options&CACHE_CLEAR) b->status = EMPTY;
- }
- }
- return 0;
- }
-
- void cache_free(struct cache *q)
- {
- struct cache_block *b = q->first;
- while (b!=NULL)
- {
- struct cache_block *next = b->next;
- free(b);
- b = next;
- }
- free(q);
- }
-
- struct cache *cache_initialize(unsigned (*drive_access)(int, unsigned,
- unsigned, void *), unsigned number_of_sectors, unsigned sector_size)
- {
- struct cache *q = malloc(sizeof(struct cache));
- if (q!=NULL)
- {
- struct cache_block *b;
- b = q->first = malloc(sizeof(struct cache_block)+sector_size-1);
- while (1)
- {
- if (b==NULL)
- {
- cache_free(q);
- return NULL;
- }
- b->status = EMPTY;
- if (--number_of_sectors == 0) break;
- b = b->next = malloc(sizeof(struct cache_block)+sector_size-1);
- }
- b->next = NULL;
- q->last = b;
- q->drive_access = drive_access;
- q->sector_size = sector_size;
- }
- return q;
- }
-
-